#ifndef PORTABLE_FORMAT_H_
#define PORTABLE_FORMAT_H_
#include <cstddef>
#include <cstdint>
#include <type_traits>

#include "src/pbc/portable-function-syntax-kind.h"
#include "src/pbc/portable-logging.h"

// PBC file format
namespace portable {
constexpr uint32_t kAlignU16String = alignof(char16_t);
constexpr uint32_t kInvalidRegister = 0x7FFFFFFF;

template <class T, int shift, int size, class U = uint32_t>
class PbcBitField final {
 public:
  static_assert(std::is_unsigned<U>::value);
  static_assert(shift < 8 * sizeof(U));  // Otherwise shifts by {shift} are UB.
  static_assert(size < 8 * sizeof(U));   // Otherwise shifts by {size} are UB.
  static_assert(shift + size <= 8 * sizeof(U));
  static_assert(size > 0);

  using FieldType = T;
  using BaseType = U;

  // A type U mask of bit field.  To use all bits of a type U of x bits
  // in a bitfield without compiler warnings we have to compute 2^x
  // without using a shift count of x in the computation.
  static constexpr int kShift = shift;
  static constexpr int kSize = size;
  static constexpr U kMask = ((U{1} << kShift) << kSize) - (U{1} << kShift);
  static constexpr int kLastUsedBit = kShift + kSize - 1;
  static constexpr U kNumValues = U{1} << kSize;
  static constexpr U kMax = kNumValues - 1;

  template <class T2, int size2>
  using Next = PbcBitField<T2, kShift + kSize, size2, U>;

  // Tells whether the provided value fits into the bit field.
  static constexpr bool is_valid(T value) {
    return (static_cast<U>(value) & ~kMax) == 0;
  }

  // Returns a type U with the bit field value encoded.
  static constexpr U encode(T value) {
    P_DCHECK(is_valid(value));
    return static_cast<U>(value) << kShift;
  }

  // Returns a type U with the bit field value updated.
  static constexpr U update(U previous, T value) {
    return (previous & ~kMask) | encode(value);
  }

  // Extracts the bit field from the value.
  static constexpr T decode(U value) {
    return static_cast<T>((value & kMask) >> kShift);
  }
};

enum class PbcValueKind : uint8_t {
  kInvalidKind = 0,
  kUniqueValue,  // for unique value such as null, undefined, empty object...
  kSmi,          // is LdaSmi.ExtraWide enough
  kDouble,
  kU8String,
  kU16String,
  kBigInt,
  kFunction,
  kObjectLiteral,
  kDefaultObjectLiteral,
  kArrayLiteral,
  kClassLiteral,
  kTemplateLiteral,
  kScope,
  // Add special kind such as HugeString, HugeBigInt
};

inline const char* GetPbcValueKindName(PbcValueKind kind) {
  switch (kind) {
    case PbcValueKind::kInvalidKind: {
      return "InvalidKind";
    }
    case PbcValueKind::kUniqueValue: {
      return "UniqueValue";
    }
    case PbcValueKind::kSmi: {
      return "Smi";
    }
    case PbcValueKind::kDouble: {
      return "Double";
    }
    case PbcValueKind::kU8String: {
      return "U8String";
    }
    case PbcValueKind::kU16String: {
      return "U16String";
    }
    case PbcValueKind::kBigInt: {
      return "BigInt";
    }
    case PbcValueKind::kFunction: {
      return "Function";
    }
    case PbcValueKind::kDefaultObjectLiteral: {
      return "DefaultObjectLiteral";
    }
    case PbcValueKind::kObjectLiteral: {
      return "ObjectLiteral";
    }
    case PbcValueKind::kArrayLiteral: {
      return "ArrayLiteral";
    }
    case PbcValueKind::kClassLiteral: {
      return "ClassLiteral";
    }
    case PbcValueKind::kTemplateLiteral: {
      return "TemplateLiteral";
    }
    case PbcValueKind::kScope: {
      return "Scope";
    }
  }
  return "unknown_kind";
}

enum UniqueTag {
  kTagInvalid = 0,
  kTagUndefined,
  kTagUninitialized,
  kTagNull,
  kTagTheHole,
  kTagTrue,
  kTagFalse,
  kTagNaN,
  kTagTopDeclarations,
  kEmptyObjectBoilerplateDescription,
  kClassFieldsSymbol,
  // TODO:(wwq) add more tag from SINGLETON_CONSTANT_ENTRY_TYPES
};

inline const char* GetUniqueTagName(UniqueTag tag) {
  switch (tag) {
    case kTagInvalid: {
      return "kTagInvalid";
    }
    case kTagUndefined: {
      return "kTagUndefined";
    }
    case kTagNull: {
      return "kTagNull";
    }
    case kTagUninitialized: {
      return "kTagUninitialized";
    }
    case kTagTheHole: {
      return "kTagTheHole";
    }
    case kTagTrue: {
      return "kTagTrue";
    }
    case kTagFalse: {
      return "kTagFalse";
    }
    case kTagNaN: {
      return "kTagNaN";
    }
    case kTagTopDeclarations: {
      return "kTagTopDeclarations";
    }
    case kEmptyObjectBoilerplateDescription: {
      return "kEmptyObjectBoilerplateDescription";
    }
    case kClassFieldsSymbol: {
      return "kClassFieldsSymbol";
    }
  }
  return "unknown_tag";
}

class PbcValue {
 public:
  using KindBits = PbcBitField<PbcValueKind, 0, 8, uint64_t>;

  static constexpr uint32_t kSize = 8;

  PbcValue() = default;

  explicit PbcValue(PbcValueKind kind) : raw_data_(KindBits::encode(kind)) {}

  PbcValueKind Kind() const { return KindBits::decode(raw_data_); }
  uint64_t raw_data_ = 0;
};

class PbcScalar : public PbcValue {
 public:
  PbcScalar() = default;

  explicit PbcScalar(PbcValueKind kind) : PbcValue(kind) {}
};

class PbcRef : public PbcValue {
 public:
  PbcRef() = default;
  explicit PbcRef(PbcValueKind kind) : PbcValue(kind) {}
};

class SmiScalar : public PbcScalar {
 public:
  using ReservedBits = KindBits::Next<uint32_t, 24>;
  using ValueBits = ReservedBits::Next<uint32_t, 32>;

  SmiScalar() = default;

  explicit SmiScalar(int32_t value) : PbcScalar(PbcValueKind::kSmi) {
    raw_data_ |= ValueBits::encode(static_cast<uint32_t>(value));
  }

  int32_t GetValue() const {
    return static_cast<int32_t>(ValueBits::decode(raw_data_));
  }
};
static_assert(sizeof(SmiScalar) == PbcValue::kSize);

class UniqueScalar : public PbcScalar {
 public:
  using ReservedBits = KindBits::Next<uint32_t, 24>;
  using TagBits = ReservedBits::Next<UniqueTag, 32>;

  static UniqueScalar Null() { return UniqueScalar(kTagNull); }
  static UniqueScalar Undefined() { return UniqueScalar(kTagUndefined); }
  static UniqueScalar Uninitialized() {
    return UniqueScalar(kTagUninitialized);
  }
  static UniqueScalar TheHole() { return UniqueScalar(kTagTheHole); }
  static UniqueScalar True() { return UniqueScalar(kTagTrue); }
  static UniqueScalar False() { return UniqueScalar(kTagFalse); }
  static UniqueScalar NaN() { return UniqueScalar(kTagNaN); }
  static UniqueScalar TopDeclarations() {
    return UniqueScalar(kTagTopDeclarations);
  }
  static UniqueScalar EmptyObjectBoilerplateDescription() {
    return UniqueScalar(kEmptyObjectBoilerplateDescription);
  }

  UniqueScalar() = default;
  explicit UniqueScalar(UniqueTag tag) : PbcScalar(PbcValueKind::kUniqueValue) {
    raw_data_ |= TagBits::encode(tag);
  }

  UniqueTag Tag() const { return TagBits::decode(raw_data_); }
};
static_assert(sizeof(UniqueScalar) == PbcValue::kSize);

class DoubleRef : public PbcRef {
 public:
  using ReservedBits = KindBits::Next<uint32_t, 24>;
  using IdBits = ReservedBits::Next<uint32_t, 32>;

  DoubleRef() = default;

  explicit DoubleRef(uint32_t id) : PbcRef(PbcValueKind::kDouble) {
    raw_data_ |= IdBits::encode(id);
  }

  uint32_t GetId() const { return IdBits::decode(raw_data_); }
};

// For both one-byte string and two-byte string
class StringRef : public PbcRef {
 public:
  using LengthBits = KindBits::Next<uint32_t, 24>;
  using OffsetBits = LengthBits::Next<uint32_t, 32>;

  StringRef() = default;

  StringRef(PbcValueKind kind, uint32_t offset, uint32_t length)
      : PbcRef(kind) {
    // TODO:(wwq) Add bound check
    P_DCHECK(kind == PbcValueKind::kU8String ||
             kind == PbcValueKind::kU16String || kind == PbcValueKind::kBigInt);
    raw_data_ |= (LengthBits::encode(length) | OffsetBits::encode(offset));
  }

  uint32_t GetOffset() const { return OffsetBits::decode(raw_data_); }

  uint32_t GetLength() const { return LengthBits::decode(raw_data_); }
};
static_assert(sizeof(StringRef) == PbcValue::kSize);

using BigIntRef = StringRef;
static_assert(sizeof(BigIntRef) == PbcValue::kSize);

class FunctionRef : public PbcRef {
 public:
  using ReservedBits = KindBits::Next<uint32_t, 8>;
  using ParameterCountBits = ReservedBits::Next<uint32_t, 16>;
  using FuncIdBits = ParameterCountBits::Next<uint32_t, 32>;

  FunctionRef() = default;

  explicit FunctionRef(uint32_t func_id, uint16_t parameter_count = 0)
      : PbcRef(PbcValueKind::kFunction) {
    raw_data_ |= FuncIdBits::encode(func_id);
    raw_data_ |= ParameterCountBits::encode(parameter_count);
  }

  uint32_t GetFuncId() const { return FuncIdBits::decode(raw_data_); }

  // ParameterCount is setted when the FunctionRef in the ConstantPool.
  uint16_t GetParameterCount() const {
    return ParameterCountBits::decode(raw_data_);
  }
};
static_assert(sizeof(FunctionRef) == PbcValue::kSize);

class ScopeRef : public PbcRef {
 public:
  using ReservedBits = KindBits::Next<uint32_t, 24>;
  using ScopeIdBits = ReservedBits::Next<uint32_t, 32>;

  static inline ScopeRef Invalid() { return ScopeRef(-1); }
  static inline uint32_t InvalidId() { return -1; }

  ScopeRef() = default;

  explicit ScopeRef(uint32_t scope_id) : PbcRef(PbcValueKind::kScope) {
    raw_data_ |= ScopeIdBits::encode(scope_id);
  }

  uint32_t GetScopeId() const { return ScopeIdBits::decode(raw_data_); }

  bool IsValid() const { return raw_data_ != Invalid().raw_data_; }
};
static_assert(sizeof(ScopeRef) == PbcValue::kSize);

class LiteralRef : public PbcRef {
 public:
  using ReservedBits = KindBits::Next<uint32_t, 24>;
  using OffsetBits = ReservedBits::Next<uint32_t, 32>;

  LiteralRef() = default;

  explicit LiteralRef(PbcValueKind kind, uint32_t offset) : PbcRef(kind) {
    raw_data_ |= OffsetBits::encode(offset);
  }

  uint32_t GetOffset() const { return OffsetBits::decode(raw_data_); }
};
static_assert(sizeof(LiteralRef) == PbcValue::kSize);

enum class PbcShortValueKind : uint8_t {
  kUniqueValue,
  kShortSmi,
  kShortU8String,
  kShortU16String,
  kShortFunction,
  kShortScope,
  kShortArrayLiteral,
  kShortObjectLiteral,
};

inline PbcValueKind ToNormalKind(PbcShortValueKind kind) {
  switch (kind) {
    case PbcShortValueKind::kUniqueValue: {
      return PbcValueKind::kUniqueValue;
    }
    case PbcShortValueKind::kShortSmi: {
      return PbcValueKind::kSmi;
    }
    case PbcShortValueKind::kShortU8String: {
      return PbcValueKind::kU8String;
    }
    case PbcShortValueKind::kShortU16String: {
      return PbcValueKind::kU16String;
    }
    case PbcShortValueKind::kShortFunction: {
      return PbcValueKind::kFunction;
    }
    case PbcShortValueKind::kShortScope: {
      return PbcValueKind::kScope;
    }
    case PbcShortValueKind::kShortArrayLiteral: {
      return PbcValueKind::kArrayLiteral;
    }
    case PbcShortValueKind::kShortObjectLiteral: {
      return PbcValueKind::kDefaultObjectLiteral;
    }
  }
}

class PbcShortValue {
 public:
  using KindBits = PbcBitField<PbcShortValueKind, 0, 3, uint32_t>;
  static constexpr uint32_t kSize = 4;

  explicit PbcShortValue(PbcShortValueKind kind)
      : raw_data_(KindBits::encode(kind)) {}

  PbcShortValueKind Kind() const { return KindBits::decode(raw_data_); }

  uint32_t raw_data_ = 0;
};
static_assert(sizeof(PbcShortValue) == PbcShortValue::kSize);

class PbcShortUnique : public PbcShortValue {
 public:
  using UniqueTagBits = KindBits::Next<UniqueTag, 29>;

  explicit PbcShortUnique(UniqueTag tag)
      : PbcShortValue(PbcShortValueKind::kUniqueValue) {
    raw_data_ |= UniqueTagBits::encode(tag);
  }

  UniqueTag Tag() const { return UniqueTagBits::decode(raw_data_); }
};
static_assert(sizeof(PbcShortUnique) == PbcShortValue::kSize);

class PbcShortSmi : public PbcShortValue {
 public:
  using ValueBits = KindBits::Next<uint32_t, 29>;

  explicit PbcShortSmi(uint32_t value)
      : PbcShortValue(PbcShortValueKind::kShortSmi) {
    P_DCHECK(value <= ValueBits::kMax);
    raw_data_ |= ValueBits::encode(value);
  }

  uint32_t GetValue() const { return ValueBits::decode(raw_data_); }
};
static_assert(sizeof(PbcShortSmi) == PbcShortValue::kSize);

class PbcShortStringRef : public PbcShortValue {
 public:
  using LengthBits = KindBits::Next<uint16_t, 9>;
  using OffsetBits = LengthBits::Next<uint32_t, 20>;

  PbcShortStringRef(bool is_u8string, uint32_t offset, uint16_t length)
      : PbcShortValue(is_u8string ? PbcShortValueKind::kShortU8String
                                  : PbcShortValueKind::kShortU16String) {
    P_DCHECK(offset <= OffsetBits::kMax);
    raw_data_ |= OffsetBits::encode(offset);
    P_DCHECK(length <= LengthBits::kMax);
    raw_data_ |= LengthBits::encode(length);
  }

  uint32_t GetOffset() const { return OffsetBits::decode(raw_data_); }

  uint16_t GetLength() const { return LengthBits::decode(raw_data_); }
};
static_assert(sizeof(PbcShortStringRef) == PbcShortValue::kSize);

class PbcShortFunction : public PbcShortValue {
 public:
  using ParameterCountBits = KindBits::Next<uint16_t, 9>;
  using FuncIdBits = ParameterCountBits::Next<uint32_t, 20>;

  explicit PbcShortFunction(uint32_t func_id, uint16_t parameter_count = 0)
      : PbcShortValue(PbcShortValueKind::kShortFunction) {
    P_DCHECK(parameter_count <= ParameterCountBits::kMax);
    raw_data_ |= ParameterCountBits::encode(parameter_count);
    P_DCHECK(func_id <= FuncIdBits::kMax);
    raw_data_ |= FuncIdBits::encode(func_id);
  }

  uint32_t GetFuncId() const { return FuncIdBits::decode(raw_data_); }

  // ParameterCount is setted when the FunctionRef in the ConstantPool.
  uint16_t GetParameterCount() const {
    return ParameterCountBits::decode(raw_data_);
  }
};
static_assert(sizeof(PbcShortFunction) == PbcShortValue::kSize);

class PbcShortScope : public PbcShortValue {
 public:
  using ScopeIdBits = KindBits::Next<uint32_t, 29>;

  explicit PbcShortScope(uint32_t scope_id)
      : PbcShortValue(PbcShortValueKind::kShortScope) {
    P_DCHECK(scope_id <= ScopeIdBits::kMax);
    raw_data_ |= ScopeIdBits::encode(scope_id);
  }

  static inline PbcShortScope Invalid() {
    return PbcShortScope(ScopeIdBits::kMax);
  }
  static inline uint32_t InvalidId() { return ScopeIdBits::kMax; }

  uint32_t GetScopeId() const { return ScopeIdBits::decode(raw_data_); }

  bool IsValid() const { return raw_data_ != Invalid().raw_data_; }
};
static_assert(sizeof(PbcShortScope) == PbcShortValue::kSize);

class PbcShortLiteralRef : public PbcShortValue {
 public:
  using OffsetBits = KindBits::Next<uint32_t, 29>;

  explicit PbcShortLiteralRef(PbcShortValueKind kind, uint32_t offset)
      : PbcShortValue(kind) {
    P_DCHECK(kind == PbcShortValueKind::kShortArrayLiteral ||
             kind == PbcShortValueKind::kShortObjectLiteral);
    P_DCHECK(offset <= OffsetBits::kMax);
    raw_data_ |= OffsetBits::encode(offset);
  }

  uint32_t GetOffset() const { return OffsetBits::decode(raw_data_); }
};
static_assert(sizeof(PbcShortLiteralRef) == PbcShortValue::kSize);

struct PbcShortProperty {
  PbcShortValue key_;
  PbcShortValue value_;
};
static_assert(sizeof(PbcShortProperty) == 8);

struct PbcProperty {
  PbcValue key_;
  PbcValue value_;
};
static_assert(sizeof(PbcProperty) == 16);

struct ObjectDescript {
  uint32_t boilerplate_description_flag_;
  uint32_t boilerplate_properties_;
  uint32_t property_length_;
  uint32_t index_keys_;
  uint8_t has_seen_proto_ : 1;
};
static_assert(sizeof(ObjectDescript) == 20);

struct RawObjectLiteral {
  ObjectDescript desc_;
  uint32_t number_of_properties_;
  struct PbcProperty properties_[];
};
static_assert(sizeof(RawObjectLiteral) == 24);

struct RawDefaultObjectLiteral {
  uint32_t number_of_properties_;
  bool is_short_value_;
  union {
    struct PbcProperty properties_[0];
    struct PbcShortProperty short_properties_[0];
  };
};
static_assert(sizeof(RawDefaultObjectLiteral) == 8);

struct ArrayLiteralFlags {
  uint8_t is_cow_ : 1;
  uint8_t kind_;
};
static_assert(sizeof(ArrayLiteralFlags) == 2);

struct RawArrayLiteral {
  ArrayLiteralFlags flags_;
  bool is_short_value_;
  uint32_t length_;
  union {
    PbcValue elements_[0];
    PbcShortValue short_elements_[0];
  };
};
static_assert(sizeof(RawArrayLiteral) == 8);

enum class ClassPropertyKind : uint8_t {
  METHOD,
  GETTER,
  SETTER,
  FIELD,
  AUTO_ACCESSOR
};

struct ClassPropertyFlag {
  uint8_t is_static_ : 1;
  uint8_t is_private_ : 1;
  uint8_t is_computed_name_ : 1;
  uint8_t kind_ : 8;
};
static_assert(sizeof(ClassPropertyFlag) == 2);

struct ClassProperty {
  ClassPropertyFlag flags_;
  PbcValue key_;
};
static_assert(sizeof(ClassProperty) == 16);

struct RawClassLiteral {
  uint32_t start_;
  uint32_t end_;
  uint64_t number_of_properties_;
  ClassProperty properties_[];
};
static_assert(sizeof(RawClassLiteral) == 16);

struct TemplateLiteralFlags {
  uint8_t duplicated_ : 1;  // Is cooked_strings_ same with raw_strings_;
};
static_assert(sizeof(TemplateLiteralFlags) == 1);

// see ast.cc::GetTemplateObject::GetOrBuildDescription
struct RawTemplateLiteral {
  TemplateLiteralFlags flags_;
  // cooked_length should be same with raw_length_
  uint32_t length_;  // length of string array
  PbcValue raw_and_cookeds_[];
};
static_assert(sizeof(RawTemplateLiteral) == 8);

enum class PbcFunctionKind : uint8_t {
  // BEGIN constructable functions
  kNormalFunction,
  kModule,
  kModuleWithTopLevelAwait,
  // BEGIN class constructors
  // BEGIN base constructors
  kBaseConstructor,
  // BEGIN default constructors
  kDefaultBaseConstructor,
  // END base constructors
  // BEGIN derived constructors
  kDefaultDerivedConstructor,
  // END default constructors
  kDerivedConstructor,
  // END derived constructors
  // END class constructors
  // END constructable functions.
  // BEGIN accessors
  kGetterFunction,
  kStaticGetterFunction,
  kSetterFunction,
  kStaticSetterFunction,
  // END accessors
  // BEGIN arrow functions
  kArrowFunction,
  // BEGIN async functions
  kAsyncArrowFunction,
  // END arrow functions
  kAsyncFunction,
  // BEGIN concise methods 1
  kAsyncConciseMethod,
  kStaticAsyncConciseMethod,
  // BEGIN generators
  kAsyncConciseGeneratorMethod,
  kStaticAsyncConciseGeneratorMethod,
  // END concise methods 1
  kAsyncGeneratorFunction,
  // END async functions
  kGeneratorFunction,
  // BEGIN concise methods 2
  kConciseGeneratorMethod,
  kStaticConciseGeneratorMethod,
  // END generators
  kConciseMethod,
  kStaticConciseMethod,
  kClassMembersInitializerFunction,
  kClassStaticInitializerFunction,
  // END concise methods 2
  kInvalid,

  kLastFunctionKind = kClassStaticInitializerFunction,
};

enum class PbcFunctionSyntaxKind : uint8_t {
#define ENUM_FIELD(NAME) k##NAME,
  FUNC_SYNTAX_KIND_LIST(ENUM_FIELD)
#undef ENUM_FIELD

      kLastFunctionSyntaxKind = kWrapped,
};

struct FunctionFlag {
  PbcFunctionKind kind_ : 8;
  PbcFunctionSyntaxKind syntax_kind_ : 8;
  uint8_t is_copyable_ : 1;
  uint8_t is_generator_ : 1;
  uint8_t is_async_ : 1;
  uint8_t is_strict_mode_ : 1;
  uint8_t is_null_name_ : 1;
  uint8_t has_exception_handler_ : 1;
  uint8_t has_debug_info_ : 1;
  uint8_t requires_instance_members_initializer_ : 1;
};
static_assert(sizeof(FunctionFlag) == 3);

constexpr uint8_t INCOMINT_NEW_TARGET_OR_GENERATOR_REGISTER_BIT = 7;
struct FunctionHeader {
  struct FunctionFlag flags_;
  uint8_t expected_nof_properties_;
  uint32_t register_count_;
  uint16_t parameter_count_;
  uint16_t length_;
  uint32_t suspend_count_;
  uint32_t exception_count_;

  uint32_t incoming_new_target_or_generator_register_
      : INCOMINT_NEW_TARGET_OR_GENERATOR_REGISTER_BIT;
  uint32_t is_u8string_ : 1;
  uint32_t name_length_ : 24;
  uint32_t name_offset_;

  uint32_t bytecode_offset_;
  uint32_t constant_pool_offset_;
};
static_assert(sizeof(FunctionHeader) == 36);

enum class VariableMode : uint8_t {
  // User declared variables:
  kLet,    // declared via 'let' declarations (first lexical)
  kConst,  // declared via 'const' declarations
  kUsing,  // declared via 'using' declaration for explicit resource management
  kAwaitUsing,  // declared via 'await using' declaration for explicit resource
                // management
                // (last lexical)
  kVar,         // declared via 'var', and 'function' declarations
  // Variables introduced by the compiler:
  kTemporary,      // temporary variables (not user-visible), stack-allocated
                   // unless the scope as a whole has forced context allocation
  kDynamic,        // always require dynamic lookup (we don't know
                   // the declaration)
  kDynamicGlobal,  // requires dynamic lookup, but we know that the
                   // variable is global unless it has been shadowed
                   // by an eval-introduced variable
  kDynamicLocal,   // requires dynamic lookup, but we know that the
                   // variable is local and where it is unless it
                   // has been shadowed by an eval-introduced
                   // variable
  // Variables for private methods or accessors whose access require
  // brand check. Declared only in class scopes by the compiler
  // and allocated only in class contexts:
  kPrivateMethod,      // Does not coexist with any other variable with the same
                       // name in the same scope.
  kPrivateSetterOnly,  // Incompatible with variables with the same name but
                       // any mode other than kPrivateGetterOnly. Transition to
                       // kPrivateGetterAndSetter if a later declaration for the
                       // same name with kPrivateGetterOnly is made.
  kPrivateGetterOnly,  // Incompatible with variables with the same name but
                       // any mode other than kPrivateSetterOnly. Transition to
                       // kPrivateGetterAndSetter if a later declaration for the
                       // same name with kPrivateSetterOnly is made.
  kPrivateGetterAndSetter,  // Does not coexist with any other variable with the
                            // same name in the same scope.
  kLastLexicalVariableMode = kAwaitUsing,
};

enum VariableKind : uint8_t {
  kNormalVariable,
  kParameterVariable,
  kThisVariable,
  kSloppyBlockFunctionVariable,
  kSloppyFunctionNameVariable,
};

struct LocalVarFlags {
  VariableMode mode_ : 4;
  VariableKind kind_ : 3;
  uint8_t needs_initialization_ : 1;
};
static_assert(sizeof(LocalVarFlags) == 1);

constexpr int VAR_NAME_BIT_LIMIT = 23;
struct LocalVar {
  uint32_t flags_ : 8;
  uint32_t is_u8stirng : 1;
  uint32_t length_ : VAR_NAME_BIT_LIMIT;
  uint32_t offset_ : 32;
};
static_assert(sizeof(LocalVar) == 8);

enum ScopeType : uint8_t {
  kScriptScope,
  kReplModeScope,
  kClassScope,
  kEvalScope,
  kFunctionScope,
  kModuleScope,
  kCatchScope,
  kBlockScope,
  kWithScope,
  kShadowRealmScope,
};

struct ScopeFlags {
  ScopeType scope_type_ : 4;
  uint8_t is_strict_mode_ : 1;
  uint8_t has_receiver_ : 1;
  uint8_t has_new_target_ : 1;
  uint8_t has_outer_scope_ : 1;
};
static_assert(sizeof(ScopeFlags) == 1);

struct RawScope {
  uint32_t flags_ : 8;  // struct ScopeFlags
  uint32_t first_local_index_ : 24;
  uint32_t function_id_;
  uint32_t outer_scope_id_;
  uint32_t num_local_vars_ : 24;
  // struct LocalVar locals_[];
};
static_assert(sizeof(RawScope) == 16);

struct RawBytecode {
  uint32_t length_;
  uint8_t bytes_[];
};
static_assert(sizeof(RawBytecode) == 4);

struct RawConstantTable {
  bool is_short_value;
  uint32_t length_;
  union {
    PbcValue entries_[0];
    PbcShortValue short_entries_[0];
  };
};
static_assert(sizeof(RawConstantTable) == 8);

struct ExceptionHandler {
  enum CatchPrediction {
    UNCAUGHT,     // The handler will (likely) rethrow the exception.
    CAUGHT,       // The exception will be caught by the handler.
    PROMISE,      // The exception will be caught and cause a promise rejection.
    ASYNC_AWAIT,  // The exception will be caught and cause a promise rejection
                  // in the desugaring of an async function, so special
                  // async/await handling in the debugger can take place.
    UNCAUGHT_ASYNC_AWAIT,  // The exception will be caught and cause a promise
                           // rejection in the desugaring of an async REPL
                           // script. The corresponding message object needs to
                           // be kept alive on the Isolate though.
  };

  explicit ExceptionHandler(uint32_t raw_data) : raw_data_(raw_data) {}

  uint32_t Offset() const { return HandlerOffsetField::decode(raw_data_); }
  uint32_t Prediction() const {
    return HandlerPredictionField::decode(raw_data_);
  }

  using HandlerPredictionField = PbcBitField<CatchPrediction, 0, 3>;
  using HandlerWasUsedField = HandlerPredictionField::Next<bool, 1>;
  using HandlerOffsetField = HandlerWasUsedField::Next<uint32_t, 28>;

  uint32_t raw_data_;
};
static_assert(sizeof(ExceptionHandler) == 4);

// TODO: need catchPrediction field?
struct ExceptionEntry {
  ExceptionEntry(uint32_t start, uint32_t end, uint32_t handler, uint32_t data)
      : start_(start), end_(end), handler_(handler), data_(data) {}

  uint32_t start_;
  uint32_t end_;
  ExceptionHandler handler_;
  uint32_t data_;
};
static_assert(sizeof(ExceptionEntry) == 16);

struct RawExceptionTable {
  uint64_t length_;
  ExceptionEntry entries_[];
};
static_assert(sizeof(RawExceptionTable) == 8);

struct RawFeedbackMeta {
  uint32_t slot_count_;
  uint32_t create_closure_slot_count_;
  uint32_t entries_[];
};
static_assert(sizeof(RawExceptionTable) == 8);

#define PBC_MAGIC 0x656C626174726F70;
#define PBC_VERSION 1;

// Must not contains virtual function
struct PbcHeader {
  uint64_t magic_;
  uint32_t version_;
  uint32_t top_level_func_id_;
  // only use if output to buffer, set by v8::base::hash_range when output
  uint64_t pbc_hash_;

  // top declarations
  uint32_t top_declarations_offset_;
  uint32_t top_declarations_size_;
  uint32_t top_declarations_count_;

  // double table
  uint32_t double_table_offset_;
  uint32_t double_table_size_;
  uint32_t double_count_;

  // string table
  uint32_t string_storage_offset_;
  uint32_t string_storage_size_;
  uint32_t string_count_;

  // u16string table
  uint32_t u16string_storage_offset_;
  uint32_t u16string_storage_size_;
  uint32_t u16string_count_;

  // bigint table
  uint32_t bigint_storage_offset_;
  uint32_t bigint_storage_size_;
  uint32_t bigint_count_;

  // literal table
  uint32_t literal_storage_offset_;
  uint32_t literal_storage_size_;
  uint32_t literal_count_;

  // scope table
  uint32_t scope_storage_offset_;
  uint32_t scope_storage_size_;
  uint32_t scope_count_;
  // local_vars_storage_offset_

  // function table
  uint32_t function_table_offset_;
  uint32_t function_table_size_;
  uint32_t function_count_;

  uint32_t bytecode_storage_offset_;
  // uint32_t bytecode_storage_size_;
};
static_assert(sizeof(PbcHeader) == 128);
}  // namespace portable

#endif  // PORTABLE_FORMAT_H_
